#include "Graphics.h"
#include "VertexBuffer.h"
#include "MathDefs.h"
#include "GraphicsDefs.h"
#include <glad/glad.h>
#include <iostream>
#include "Object.h"

static const unsigned UI_VERTEX_SIZE = 8;

VertexBuffer::VertexBuffer(Context* context) : Object(context_)
{
    vertexSize_ = UI_VERTEX_SIZE * 4;
}

VertexBuffer::~VertexBuffer()
{
    Release();
}

void VertexBuffer::SetVertexSize(unsigned size)
{
    vertexSize_ = size;
}

bool VertexBuffer::SetSize(unsigned vertexCount, bool dynamic)
{

    vertexCount_ = vertexCount;
    dynamic_ = dynamic;

    if (vertexCount_ && vertexSize_)
    {
        if (data_)
        {
            delete[] data_;
            data_ = nullptr;
        }
        data_ = new unsigned char[vertexCount_ * vertexSize_];
    }
       
    return Create();
}

    void VertexBuffer::Release()
    {
        if (vbo_)
        {
            context_->GetGraphics()->SetVBO(0);
            glDeleteBuffers(1, &vbo_);
            vbo_ = 0;
        }

        if (data_)
        {
            delete[] data_;
            data_ = nullptr;
        }
    }

    bool VertexBuffer::SetData(const void* data)
    {
        if (!data)
        {
            assert(0);
            return false;
        }

        if (!vertexSize_)
        {
            assert(0);
            return false;
        }

        if (data_ && data != data_)
            memcpy(data_, data, vertexCount_ * (size_t)vertexSize_);

        if (vbo_)
        {
            if (context_->GetGraphics())
            {
                context_->GetGraphics()->SetVBO(vbo_);
                glBufferData(GL_ARRAY_BUFFER, vertexCount_ * (size_t)vertexSize_, data, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
            }
        }
        return true;
    }

    bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard)
    {
        if (start == 0 && count == vertexCount_)
            return SetData(data);

        if (!data)
        {
            assert(0);
            return false;
        }

        if (!vertexSize_)
        {
            assert(0);
            return false;
        }

        if (start + count > vertexCount_)
        {
            assert(0);
            return false;
        }

        if (!count)
            return true;

        if (data_ && data_ + start * vertexSize_ != data)
            memcpy(data_ + start * vertexSize_, data, count * (size_t)vertexSize_);

        if (vbo_)
        {
            if (!context_->GetGraphics())
            {
                context_->GetGraphics()->SetVBO(vbo_);
                if (!discard || start != 0)
                    glBufferSubData(GL_ARRAY_BUFFER, start * (size_t)vertexSize_, count * vertexSize_, data);
                else
                    glBufferData(GL_ARRAY_BUFFER, count * (size_t)vertexSize_, data, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
            }
        }

        return true;
    }

	bool VertexBuffer::Create()
    {
        if (!vertexCount_)
        {
            Release();
            return true;
        }

        if (context_->GetGraphics())
        {
            if (!vbo_)
                glGenBuffers(1, &vbo_);
            if (!vbo_)
            {
                assert(0);
                return false;
            }

            context_->GetGraphics()->SetVBO(vbo_);
            glBufferData(GL_ARRAY_BUFFER, vertexCount_ * (size_t)vertexSize_, nullptr, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
        }

        return true;
    }

    bool VertexBuffer::UpdateToGPU()
    {
        if (vbo_ && data_)
            return SetData(data_);
        else
            return false;
    }

